home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockPhotobucketService.js < prev    next >
Text File  |  2007-10-18  |  69KB  |  2,007 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. const ENABLE_DEBUG = true;
  18. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockPhotobucketService: "+x+"\n"); }
  19.  
  20. const Cc = Components.classes;
  21. const Ci = Components.interfaces;
  22. const Cr = Components.results;
  23. const Cu = Components.utils;
  24.  
  25. Cu.import("resource:///modules/FlockCryptoHash.jsm");
  26.  
  27. const PHOTOBUCKET_TITLE             = "PhotoBucket Web Service";
  28. const PHOTOBUCKET_FAVICON           = "chrome://flock/content/services/photobucket/favicon.png";
  29. const PHOTOBUCKET_CID               = Components.ID('{d3b147b0-a321-11da-a746-0800200c9a66}');
  30. const PHOTOBUCKET_CONTRACTID        = "@flock.com/?photo-api-photobucket;1";
  31. const SERVICE_ENABLED_PREF          = "flock.service.photobucket.enabled";
  32. const CATEGORY_COMPONENT_NAME       = "Photobucket JS Component"
  33. const CATEGORY_ENTRY_NAME           = "photobucket"
  34.  
  35. const flockIError                   = Components.interfaces.flockIError;
  36. const flockIPhotoAlbum              = Components.interfaces.flockIPhotoAlbum;
  37. const flockIPhotoPerson             = Components.interfaces.flockIPhotoPerson;
  38.  
  39. const FLOCK_PHOTO_ALBUM_CONTRACTID  = "@flock.com/photo-album;1";
  40. const FLOCK_PHOTOPERSON_CONTRACTID  = "@flock.com/photo-person;1";
  41. const FLOCK_ERROR_CONTRACTID        = "@flock.com/error;1";
  42.  
  43. const PHOTOBUCKET_API_VERSION       = "1.0";
  44. const PHOTOBUCKET_TOKEN_PREF        = "flock.photo.photobucket.token";
  45. const PHOTOBUCKET_SESSION_REFRESH_INTERVAL = 21600000; // every 6 hours refresh the session token
  46.  
  47. const PHOTOBUCKET_PK                = "e57335b0847da8b1105b6c8bc27d217a";
  48. const PHOTOBUCKET_SCID              = "149825788";
  49. const PHOTOBUCKET_PROPERTIES        = "chrome://flock/locale/services/photobucket.properties";
  50.  
  51. // services
  52. const RDFS = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
  53. const IOS = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  54. const RDFCU = Cc["@mozilla.org/rdf/container-utils;1"].getService(Ci.nsIRDFContainerUtils);
  55.  
  56. var gCompTK;
  57. function getCompTK() {
  58.   if (!gCompTK) {
  59.     gCompTK = Cc["@flock.com/singleton;1"]
  60.                         .getService(Ci.flockISingleton)
  61.                         .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  62.                         .wrappedJSObject;
  63.   }
  64.   return gCompTK;
  65. }
  66.  
  67. // for Cardinal to Danphe migration
  68. const FLOCK_NS = "http://flock.com/rdf#";
  69.  
  70. // String defaults... may be updated later through Web Detective
  71. var gStrings = {
  72.   "domains": "photobucket.com",
  73.   "userloginURL": "http://photobucket.com/login.php",
  74.   "userprofileURL": "http://photobucket.com/images/search/%accountid%",
  75.   "serviceloginURL": "http://photobucket.com/svc/servicelogin.php",
  76.   "apiURL": "http://photobucket.com/svc/api.php"
  77. };
  78.  
  79. var gPhotobucketAPI;
  80.  
  81. // ====================================================
  82. // ========== BEGIN General Helper Functions ==========
  83. // ====================================================
  84.  
  85. function loadSubScript(spec)
  86. {
  87.   var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
  88.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  89.   var context = {};
  90.   loader.loadSubScript(spec, context);
  91.   return context;
  92. }
  93.  
  94. function loadLibraryFromSpec(aSpec) {
  95.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  96.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  97.   loader.loadSubScript(aSpec);
  98. }
  99.  
  100. loadLibraryFromSpec("chrome://flock/content/common/flocksafe.js");
  101. loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
  102.  
  103.  
  104. function photobucketPhoto() {
  105. }
  106.  
  107. photobucketPhoto.prototype= {
  108.   id: "",
  109.   thumbnail: "",
  110.   webPageUrl: "",
  111.   midSizePhoto: "",
  112.   largeSizePhoto: "",
  113.   title: "",
  114.   username: "",
  115.   userid: "",
  116.   is_video: "",
  117.   svcShortName: 'photobucket',
  118.  
  119.   buildTooltip: function( ) {
  120.     // do we have to use document from the window to ceate elements? -- ja
  121.     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  122.                        .getService(Components.interfaces.nsIWindowMediator);
  123.     var win = wm.getMostRecentWindow('navigator:browser');
  124.     if (!win) return null;
  125.  
  126.     var hbox = win.document.createElement('hbox');
  127.     var box = win.document.createElement('vbox');
  128.     box.setAttribute('style', 'max-width: 250px');
  129.     var title = win.document.createElement('label');
  130.     title.setAttribute('value', this.title );
  131.     title.setAttribute('crop', 'end');
  132.     box.appendChild(title);
  133.     var lbl = win.document.createElement('label');
  134.     lbl.setAttribute('value', this.username );
  135.     lbl.setAttribute('class', 'user');
  136.     box.appendChild(lbl);
  137.     hbox.appendChild(box)
  138.  
  139.     var vbox = win.document.createElement('vbox');
  140.     var cbox = win.document.createElement('hbox');
  141.     var largeImg = win.document.createElement('image');
  142.     largeImg.setAttribute('src', this.midSizePhoto);
  143.     largeImg.setAttribute('style', 'margin-bottom: 2px;');
  144.     var spacer = win.document.createElement('spacer'); 
  145.     spacer.setAttribute('flex', '1');
  146.     cbox.appendChild(largeImg);
  147.     cbox.appendChild(spacer);
  148.     vbox.appendChild(cbox);
  149.     vbox.appendChild(hbox);
  150.  
  151.     return vbox;
  152.   },
  153.   buildHTML: function( ) {
  154.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.midSizePhoto+'" border="0" /></a>';
  155.   },
  156.   buildLargeHTML: function( ) {
  157.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.largeSizePhoto+'" border="0" /></a>';
  158.   },
  159.   buildBBCode: function ( ) {
  160.     if(this.is_video)
  161.     {
  162.       return '[photob]' + this.largeSizePhoto + '[photob]'
  163.       
  164.     }else
  165.     {
  166.       return '[url=' + this.webPageUrl + '][img]'+ this.largeSizePhoto +'[/img][/url]';
  167.     }
  168.   },
  169.   buildMiniPage: function ( ) {
  170.     return '<html><head><title>' + this.title + ' (' + this.username + ')</title></head>' +
  171.            '<body><object width="425" height="350">' +
  172.            '<param name="movie" value="'+this.largeSizePhoto+'"/>' +
  173.            '<center><embed src="'+this.largeSizePhoto+'&autoplay=1" type="application/x-shockwave-flash" width="425" height="350"/></object></center></body></html>';
  174.   },
  175.   QueryInterface: function(iid) {
  176.     if (!iid.equals(Components.interfaces.nsISupports) &&
  177.         !iid.equals(Components.interfaces.flockIPhoto)) {
  178.       throw Components.results.NS_ERROR_NO_INTERFACE;
  179.     }
  180.     return this;
  181.   }
  182. };
  183.  
  184.  
  185. // ====================================================
  186. // ========== BEGIN photobucketService class ==========
  187. // ====================================================
  188.  
  189. function photobucketService()
  190. {
  191.   this.obs = Components.classes["@mozilla.org/observer-service;1"]
  192.                        .getService(Components.interfaces.nsIObserverService);
  193.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  194.                            .getService(Components.interfaces.flockIAccountUtils);
  195.   this.url = "http://www.photobucket.com";
  196.   this.initialized = false;
  197.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  198.   this.logger.init("photobucket");
  199.   this._ctk = {
  200.     interfaces: [
  201.       "nsISupports",
  202.       "nsIClassInfo",
  203.       "nsISupportsCString",
  204.       "nsIObserver",
  205.       "flockIPollingService",
  206.       "flockIWebService",
  207.       "flockIAuthenticateNewAccount",
  208.       "flockIManageableWebService",
  209.       "flockIMediaWebService"
  210.     ],
  211.     shortName: "photobucket",
  212.     fullName: "Photobucket",
  213.     description: PHOTOBUCKET_TITLE,
  214.     favicon: PHOTOBUCKET_FAVICON,
  215.     contractID: PHOTOBUCKET_CONTRACTID,
  216.     accountClass: photobucketAccount,
  217.     needPassword: false
  218.   };
  219.  
  220.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  221.             .getService(Components.interfaces.nsIStringBundleService);
  222.   var bundle = sbs.createBundle(PHOTOBUCKET_PROPERTIES);
  223.  
  224.   this._channels = {
  225.     "special:recent": {
  226.       title: bundle.GetStringFromName("flock.photobucket.title.recent"),
  227.       supportsSearch: false
  228.     }
  229.   };
  230.  
  231.   this.init();
  232. }
  233.  
  234.  
  235. // BEGIN nsIObserver interface
  236. photobucketService.prototype.observe =
  237. function photobucketService_observe(aSubject, aTopic, aData)
  238. {
  239. }
  240. // END nsIObserver interface
  241.  
  242.  
  243. photobucketService.prototype.shortName = "photobucket";
  244. photobucketService.prototype.serviceName = "Photobucket";
  245.  
  246. photobucketService.prototype.getPhotoFromRDFNode =
  247. function (aRDFId)
  248. {
  249.   var newPhoto = new photobucketPhoto();
  250.   var coopPhoto = this.faves_coop.get(aRDFId);
  251.   newPhoto.webPageUrl = coopPhoto.URL;
  252.   newPhoto.thumbnail = coopPhoto.thumbnail;
  253.   newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
  254.   newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
  255.   newPhoto.username = coopPhoto.username;
  256.   newPhoto.userid = coopPhoto.userid;
  257.   newPhoto.title = coopPhoto.name;
  258.   newPhoto.id = coopPhoto.photoid;
  259.   newPhoto.icon = coopPhoto.favicon;
  260.   newPhoto.uploadDate = coopPhoto.datevalue;
  261.   newPhoto.is_public = coopPhoto.is_public;
  262.   newPhoto.is_video = coopPhoto.is_video;
  263.   return newPhoto;
  264. }
  265.  
  266. photobucketService.prototype.handlePhotosResult =
  267. function photobucketService_handlePhotosResult(aXML) {
  268.   var inst = this;
  269.   function createItem(media, is_video) {
  270.     var uploadDate = media.getAttribute("uploaddate");
  271.     var title = media.getAttribute("name");
  272.     var username = media.getAttribute("username");
  273.     var page_url = "";
  274.     var thumb_url = media.getElementsByTagName('thumb')[0].firstChild.nodeValue;
  275.     var small_url = media.getElementsByTagName('url')[0].firstChild.nodeValue;
  276.     // this is a photobucket api issue - need a fix -
  277.     try {
  278.       page_url = media.getElementsByTagName('browseurl')[0].firstChild.nodeValue;
  279.       page_url = page_url.replace('&', '&');
  280.     }
  281.     catch(e) {
  282.       page_url = small_url;
  283.     }
  284.     var newPhoto = new photobucketPhoto();
  285.     newPhoto.webPageUrl = page_url;
  286.     newPhoto.thumbnail = thumb_url;
  287.     newPhoto.midSizePhoto = thumb_url;
  288.     newPhoto.largeSizePhoto = small_url;
  289.     newPhoto.title = title;
  290.     newPhoto.username = username;
  291.     newPhoto.userid = username;
  292.     if (uploadDate) {
  293.       newPhoto.id = uploadDate;
  294.       newPhoto.uploadDate = parseInt(uploadDate)*1000;
  295.     } else {
  296.       newPhoto.id = username + ":" + title;
  297.     }
  298.     newPhoto.is_video = is_video;
  299.     newPhoto.has_miniView = is_video;
  300.     return newPhoto;
  301.   }
  302.  
  303.   try {
  304.     var rval = [];
  305.  
  306.     var videoList = aXML.getElementsByTagName("video");
  307.     for (var i = 0; i < videoList.length; i++) {
  308.       rval.push(createItem(videoList[i], true));
  309.     }
  310.  
  311.     var photoList = aXML.getElementsByTagName("photo");
  312.     for (var i = 0; i < photoList.length; i++) {
  313.       rval.push(createItem(photoList[i], false));
  314.     }
  315.   } catch(e) {
  316.     inst.logger.error(e);
  317.   }
  318.  
  319.   /*
  320.    * do not sort anymore since PB no longer passes back the uploadDate.
  321.    * instead we just present the photos in the order PB gives them to
  322.    * us (upload order)
  323.    * see https://bugzilla.flock.com/show_bug.cgi?id=9084
  324.  
  325.   // sort the photos and videos together by date
  326.   // the follow sorts based on ID which we are using the uploadDate to be the id
  327.   // it is ugly -- and probably not as efficient as it could be --- but it
  328.   // is only sorting 20 items at most so it is good enough for now - ja
  329.   rval.sort(function(a,b) {
  330.     return (b.id-a.id);
  331.   });
  332.   */
  333.  
  334.   return rval;
  335. }
  336.  
  337. photobucketService.prototype.getContacts =
  338. function photobucketService_getContacts(aListener)
  339. {
  340.   return -1;  // throw unsupported exception
  341. }
  342.  
  343. photobucketService.prototype.findByUsername =
  344. function photobucketService_findByUsername(aListener, aUsername)
  345. {
  346.   var inst = this;
  347.   var myListener = {
  348.     onResult: function (aXML) {
  349.       //var user = aXML.getElementsByTagName("photo")[0];
  350.       // We should be getting all the user's details from the response xml, but
  351.       // the pbucket response has all pertinent info with the photo tag.  If
  352.       // there are no photos, then we have no user info
  353.       var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
  354.                                  .createInstance(flockIPhotoPerson);
  355.       newUserObj.id = aUsername;
  356.       newUserObj.username = aUsername;
  357.       newUserObj.fullname = aUsername;
  358.       newUserObj.service = inst;
  359.       aListener.onFindByUsernameResult(newUserObj);
  360.     },
  361.     onError: function (aXML) {
  362.       aListener.onError(aXML);
  363.     }
  364.   }
  365.   var params = {};
  366.   params.username = aUsername;
  367.   var dict = params2Dictionary(params);
  368.   this.api.authenticatedCall(myListener, "getrecentusermedia", dict);
  369. }
  370.  
  371. function MultiGetter() {
  372. }
  373.  
  374. MultiGetter.prototype = {
  375.   mTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer),
  376.   init: function(aSvc, aListener, aEnumerator) {
  377.     this.mHasNew = false;
  378.     this.mNeedSave = false;
  379.     this.mSvc = aSvc;
  380.     this.mListener = aListener;
  381.     this.mArray = new Array();
  382.     while (aEnumerator.hasMoreElements()) {
  383.       var p = aEnumerator.getNext();
  384.       p.QueryInterface(Components.interfaces.flockIPhotoPerson);
  385.  
  386.       this.mArray.push(p);
  387.     }
  388.     this.next();
  389.   },
  390.   finish: function() {
  391.     //dump("FINITI\n");
  392.     if (this.mHasNew) {
  393.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_HAS_NEW);
  394.     }
  395.     else if (this.mNeedSave) {
  396.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NEED_SAVE);
  397.     }
  398.     else {
  399.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NO_CHANGE);
  400.     }
  401.     return;
  402.   },
  403.   updatePerson: function(aPerson, aPhoto) {
  404.     //dump(aPerson.username + "updating person\n");
  405.     var seq = aPerson.seq;
  406.     var newSeq = parseInt(aPhoto.id);
  407.     if (newSeq > seq) {
  408.       var oldHasNew = aPerson.hasNew;
  409.       aPerson.seq = aPhoto.id;
  410.       this.mNeedSave = true;
  411.       if (seq != 0) {
  412.         this.mHasNew = true;
  413.         aPerson.hasNew = true
  414.         if (oldHasNew == false) {
  415.           aPerson.lastNewSeq = seq + 1;
  416.         }
  417.       }
  418.     }
  419.   },
  420.   notify: function() {
  421.     this.doNext();
  422.   },
  423.   next: function() {
  424.     this.mTimer.initWithCallback(this, 1000, 0);
  425.   },
  426.   doNext: function() {
  427.     //dump("NEXT PLEASE\n");
  428.     var inst = this;
  429.  
  430.     if (!this.mArray.length) {
  431.       this.finish();
  432.       return;
  433.     }
  434.     var person = this.mArray.pop();
  435.  
  436.     var listener = {
  437.       onSearchResult: function(aEnumerator) {
  438.       //dump("b search result\n");
  439.         while (aEnumerator.hasMoreElements()) {
  440.           var photo = aEnumerator.getNext();
  441.           inst.updatePerson(person, photo);
  442.           break;
  443.         }
  444.         inst.next();
  445.       },
  446.       onError: function(aError) {
  447.       //dump("b search eror\n");
  448.         inst.finish();
  449.       }
  450.     }
  451.     this.mSvc.search(listener, person.id, "", "", 1, 1);
  452.   }
  453. }
  454.  
  455. photobucketService.prototype.getMostRecentPhotoForList =
  456. function photobucketService_getMostRecentPhotoForList(aListener, aEnumerator)
  457. {
  458.   var mg = new MultiGetter();
  459.   mg.init(this, aListener, aEnumerator);
  460. }
  461.  
  462. photobucketService.prototype.supportsSearch =
  463. function photobucketService_supportsSearch( aQueryString ) {
  464.  
  465.   var aQuery = new queryHelper(aQueryString);
  466.  
  467.   if (aQuery.special) {
  468.     var channel = this._channels["special:" + aQuery.special];
  469.     if (channel) {
  470.       return channel.supportsSearch;
  471.     }
  472.   }
  473.  
  474.   if (aQuery.user)
  475.     return true;
  476.   if (aQuery.search)
  477.     return false;
  478.   return null;
  479. }
  480.  
  481.  
  482. photobucketService.prototype.queryChannel =
  483. function photobucketService_queryChannel(aListener, aQueryString, aCount, aPage)
  484. {
  485.   var aQuery = new queryHelper(aQueryString);
  486.   var inst = this;
  487.  
  488.   var myListener = {
  489.     onResult: function (aXML) {
  490.       var rval = inst.handlePhotosResult(aXML);
  491.       var enum_ = {
  492.         hasMoreElements: function() {
  493.           return (rval.length > 0);
  494.         },
  495.         getNext: function() {
  496.           return rval.shift();
  497.         }
  498.       }
  499.       aListener.onSearchResult(enum_);
  500.     },
  501.     onError: function (aError) {
  502.       // if we get an error here this probably means user inputted an illegal character
  503.       // do nothign and let the "Sorry, no results found" message appear
  504.     }
  505.   }
  506.  
  507.   var params = {
  508.     page: aPage,
  509.     num: aCount
  510.   }
  511.  
  512.   if (aQuery.getKey('special') == 'recent') {
  513.     var dict = params2Dictionary(params);
  514.     this.api.call(myListener, "getrecentphotos", dict);
  515.   }
  516.  
  517.   if (aQuery.hasKey('search')) {
  518.     // don't pass in encoded text for PB
  519.     // if an illegal char is used, we will return no results
  520.     var aText = aQuery.search;
  521.     if (aText && aText.length) {
  522.       params.query = aText.split(".")[0];
  523.       var dict = params2Dictionary(params);
  524.       this.api.call(myListener, "search", dict);
  525.     }
  526.   }
  527. }
  528.  
  529. photobucketService.prototype.search =
  530. function photobucketService_search(aListener, aQueryString, aCount, aPage)
  531. {
  532.   var aQuery = new queryHelper(aQueryString);
  533.   if (!aQuery.user) {
  534.        this.queryChannel(aListener, aQueryString, aCount, aPage);
  535.        return;
  536.   }
  537.   var aUserid = aQuery.user;
  538.  
  539.   var inst = this;
  540.  
  541.   var myListener = {
  542.     onResult: function (aXML) {
  543.       var rval = inst.handlePhotosResult(aXML);
  544.       var enum_ = {
  545.         hasMoreElements: function() {
  546.           return (rval.length > 0);
  547.         },
  548.         getNext: function() {
  549.           return rval.shift();
  550.         }
  551.       }
  552.       aListener.onSearchResult(enum_);
  553.     },
  554.     onError: function (aError) {
  555.       aListener.onError(aError);
  556.     }
  557.   }
  558.  
  559.   var params = {};
  560.  
  561.  
  562.   if (aUserid) {
  563.     params.username = aUserid;
  564.     params.perpage = aCount;
  565.   }
  566.  
  567.   if (aQuery.search) params.query = aQuery.getEncodedKey('search').split(".")[0];
  568.   if (aPage) params.page = aPage;
  569.   if (aQuery.album) params.album_name = aQuery.album;
  570.  
  571.   params.media = 'all';
  572.  
  573.   var dict = params2Dictionary(params);
  574.   if (params.query != null) {
  575.     this.api.call(myListener, "search", dict);
  576.   } else if (params.username != null) {
  577.     if (params.album_name) {
  578.       this.api.authenticatedCall(myListener, "getuseralbumpaginated", dict);
  579.     }else
  580.       this.api.authenticatedCall(myListener, "getrecentusermedia", dict);
  581.   }
  582.  
  583. }
  584.  
  585. photobucketService.prototype.createAlbum =
  586. function photobucketService_createAlbum(aListener, aPath)
  587. {
  588.   var svc = this;
  589.   this.logger.debug("createAlbum - aPath = "+aPath);
  590.   var albumCreationListener = {
  591.     onResult: function(aXML) {
  592.       svc.logger.info(aPath + " album successfully created");
  593.       var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID]
  594.                                .createInstance(flockIPhotoAlbum);
  595.       var newAlbumPath;
  596.       if (params.parent_album_name) {
  597.         newAlbumPath = params.parent_album_name + params.new_album_name;
  598.       } else {
  599.         newAlbumPath = params.new_album_name;
  600.       }
  601.       newAlbum.title = newAlbumPath;
  602.       newAlbum.id = newAlbumPath;
  603.       aListener.onCreateAlbum(newAlbum);
  604.     },
  605.     onError: function(aError) {
  606.       svc.logger.error("Error in album creation");
  607.       aListener.onError(aError);
  608.     }
  609.   }
  610.  
  611.   var parentDir = "";
  612.   var pathName = aPath.split('/');
  613.   var params = new Object();
  614.   params.new_album_name = pathName.pop();
  615.   var dict;
  616.  
  617.   for (var i = 0; i < pathName.length; i++) {
  618.     parentDir += pathName[i] + '/';
  619.   }
  620.  
  621.   if (parentDir.length) {
  622.     params.parent_album_name = parentDir;
  623.   }
  624.  
  625.   dict = params2Dictionary(params);
  626.   this.api.authenticatedCall(albumCreationListener, "createalbum", dict);
  627. }
  628.  
  629. photobucketService.prototype.handleAlbumsResult =
  630. function photobucketService_handleAlbumsResult(aXML)
  631. {
  632.   var rval = new Array();
  633.   var albumList = aXML.getElementsByTagName("photoalbum");
  634.   for (var i = 0; i < albumList.length; i++) {
  635.     var album = albumList[i];
  636.     var title = album.getAttribute("name");
  637.  
  638.     // the first album node has no name attr, so skip it
  639.     if (!title) continue;
  640.  
  641.     var username = album.getAttribute('username');
  642.     var id;
  643.     // parse out the prefix /username/
  644.     if (title.match(album.getAttribute('username')) && title != username) {
  645.       id = title.substring(username.length + 1);
  646.       var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID]
  647.                                .createInstance(flockIPhotoAlbum);
  648.       newAlbum.id = id;
  649.       newAlbum.title = id;
  650.       rval.push(newAlbum);
  651.     }
  652.   }
  653.   return rval;
  654. }
  655.  
  656. photobucketService.prototype.getAccountStatus =
  657. function photobucketService_getAccountStatus(aListener)
  658. {
  659.   var inst = this;
  660.   var myListener = {
  661.     onResult: function(aXML) {
  662.       try {
  663.         var result = Cc["@mozilla.org/hash-property-bag;1"]
  664.                      .createInstance(Ci.nsIWritablePropertyBag2);
  665.         var username = aXML.getElementsByTagName("username")[0]
  666.                            .firstChild
  667.                            .nodeValue;
  668.         var maxSpace = aXML.getElementsByTagName("megabytes_allowed")[0]
  669.                            .firstChild
  670.                            .nodeValue;
  671.         var usedSpace = aXML.getElementsByTagName("megabytes_used")[0]
  672.                             .firstChild
  673.                             .nodeValue;
  674.         var isPremium = aXML.getElementsByTagName("premium")[0]
  675.                             .firstChild
  676.                             .nodeValue;
  677.         result.setPropertyAsAString("username", username);
  678.         result.setPropertyAsAString("maxSpace", maxSpace);
  679.         result.setPropertyAsAString("usedSpace", usedSpace);
  680.         result.setPropertyAsAString("maxFileSize", "");
  681.         result.setPropertyAsAString("usageUnits", "megabytes");
  682.         result.setPropertyAsBool("isPremium", (isPremium == "1"));
  683.         aListener.onSuccess(result, "");
  684.       } catch (ex) {
  685.         inst.logger.error('getaccountstatus error :' + ex);
  686.       }
  687.     },
  688.     onError: function (aError) {
  689.       aListener.onError(aError);
  690.     }
  691.   }
  692.   var params = new Object();
  693.   var dict = params2Dictionary(params);
  694.   this.api.authenticatedCall(myListener, "getuserinfo", dict);
  695. }
  696.  
  697. photobucketService.prototype.getAlbums =
  698. function photobucketService_getAlbums(aListener, aUsername)
  699. {
  700.   var inst = this;
  701.   var myListener = {
  702.     onResult: function (aXML) {
  703.       try {
  704.         var rval = inst.handleAlbumsResult(aXML);
  705.         var enum_ = {
  706.           hasMoreElements: function() {
  707.             return (rval.length > 0);
  708.           },
  709.           getNext: function() {
  710.             return rval.shift();
  711.           }
  712.         }
  713.         aListener.onGetAlbumsResult(enum_);
  714.       } catch(e) {
  715.         inst.logger.error(e);
  716.         aListener.onError(null);
  717.       }
  718.     },
  719.     onError: function (aXML) {
  720.       aListener.onError(aXML);
  721.     }
  722.   }
  723.   var params = new Object();
  724.   params.recurse = "true";
  725.   params.view = "flat"
  726.   params.media = "none";
  727.   if (aUsername) {
  728.     params.username = aUsername;
  729.   }
  730.   var dict = params2Dictionary(params);
  731.   this.api.authenticatedCall(myListener, "getuseralbum", dict);
  732. }
  733.  
  734. photobucketService.prototype.mUploader = null;
  735. photobucketService.prototype.icon = PHOTOBUCKET_FAVICON;
  736.  
  737. /* features for service */
  738. photobucketService.prototype.supportsFeature =
  739. function photobucketService_supportsFeature(aFeature)
  740. {
  741.   var supports = {};
  742.   supports.tags = false;
  743.   supports.title = false;
  744.   supports.contacts = false;
  745.   supports.privacy = false;
  746.   supports.fileName = true;
  747.   supports.albumCreation = true;
  748.   return (supports[aFeature] == true);
  749. }
  750.  
  751. photobucketService.prototype.mSupportsTitle = false;
  752. photobucketService.prototype.mSupportsTags = false;
  753. photobucketService.prototype.mSupportsContacts = false;
  754. photobucketService.prototype.mSupportsPrivacey = false;
  755.  
  756. photobucketService.prototype.init =
  757. function photobucketService_init()
  758. {
  759.   if (this.initialized) return;
  760.   this.initialized = true;
  761.  
  762.   DEBUG(".init()");
  763.  
  764.   this.prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  765.   if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  766.        !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
  767.   {
  768.     DEBUG("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
  769.     var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  770.     catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
  771.     catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
  772.     catMgr.deleteCategoryEntry("flockMediaProvider", CATEGORY_ENTRY_NAME, true);
  773.     return;
  774.   }
  775.  
  776.   this.mAuthUser = {};
  777.  
  778.   this.faves_coop = Components.classes["@flock.com/singleton;1"]
  779.                               .getService(Components.interfaces.flockISingleton)
  780.                               .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  781.                               .wrappedJSObject;
  782.  
  783.   this.urn = 'urn:photobucket:service';
  784.   this.pbService = new this.faves_coop.Service(
  785.     this.urn,
  786.     {
  787.       name: "photobucket",
  788.       desc: "The Photobucket Service",
  789.       contactLabel: 'People'
  790.     }
  791.   );
  792.   this.pbService.serviceId = PHOTOBUCKET_CONTRACTID;
  793.   if (!gPhotobucketAPI) {
  794.     gPhotobucketAPI = new PhotobucketAPI();
  795.   }
  796.   this.api = gPhotobucketAPI;
  797.  
  798.   // Load Web Detective file
  799.   this.webDetective = this.acUtils.useWebDetective("photobucket.xml");
  800.   for (var s in gStrings) {
  801.     gStrings[s] = this.webDetective.getString("photobucket", s, gStrings[s]);
  802.   }
  803.   this.mApiUrl = gStrings["apiURL"];
  804.   this.pbService.domains = gStrings["domains"];
  805.   this.pbService.loginURL = gStrings["userloginURL"];
  806.  
  807.   // Update auth states
  808.   try {
  809.     if (this.webDetective.detectCookies("photobucket", "loggedout", null)) {
  810.       this.acUtils.markAllAccountsAsLoggedOut(PHOTOBUCKET_CONTRACTID);
  811.       this.mAuthUser = null;
  812.       this.mUploader = null;
  813.     } else {
  814.       this.checkPersistedToken();
  815.     }
  816.   } catch (ex) {
  817.     this.logger.error("ERROR updating auth states for Photobucket: "+ex);
  818.   }
  819. }
  820.  
  821. photobucketService.prototype.checkPersistedToken =
  822. function photobucketService_checkPersistedToken()
  823. {
  824.   var acctURN = this.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  825.   if (!acctURN) { return; }
  826.   var token = this.faves_coop.get(acctURN).authToken;
  827.   if (token) {
  828.     this.logger.debug('checking if the session associated with the token is still valid ... ' + token);
  829.     // try to get the session from the persisted token
  830.     var inst = this;
  831.     var myListener = {
  832.       onResult: function(aXML) {
  833.         try {
  834.           var resp = aXML.getElementsByTagName('response');
  835.           if (resp[0].getAttribute('stat') == 'ok') {
  836.             var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  837.             var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
  838.             inst.api.mApiUrl = aXML.getElementsByTagName("url")[0].firstChild.nodeValue;
  839.             inst.getAuthUser().id = username;
  840.             inst.getAuthUser().username = username;
  841.             inst.getAuthUser().fullname = username;
  842.             inst.getAuthUser().sessionkey = sessionkey;
  843.             if (!inst.api.mSessionRefreshTimer) inst.api.fireSessionRefreshTimer();
  844.           } else {
  845.             inst.mAuthUser = null;
  846.             this.mUploader = null;
  847.           }
  848.         }
  849.         catch(e) {
  850.           inst.logger.error(e);
  851.         }
  852.       },
  853.       onError: function(aErr) {
  854.         return false;
  855.       }
  856.     }
  857.     var params = {};
  858.     params.service_token = token;
  859.     inst.call(myListener, "getsession", params2Dictionary(params));
  860.   }
  861.   //this.mAuthUser.sessionkey = null;
  862. }
  863.  
  864. photobucketService.prototype.getAuthState =
  865. function photobucketService_getAuthState()
  866. {
  867.   return this.state;
  868. }
  869.  
  870. photobucketService.prototype.__defineGetter__("supportsTitle", function () { return this.mSupportsTitle; })
  871. photobucketService.prototype.__defineGetter__("supportsTags", function () { return this.mSupportsTags; })
  872. photobucketService.prototype.__defineGetter__("supportsContacts", function () { return this.mSupportsContacts; })
  873. photobucketService.prototype.__defineGetter__("supportsPrivacey", function () { return this.mSupportsPrivacey; })
  874. photobucketService.prototype.__defineGetter__("authState", function () { return this.state; })
  875.  
  876. photobucketService.prototype.logout =
  877. function photobucketService_logout()
  878. {
  879.   this.mAuthUser = null;
  880.   this.mUploader = null;
  881.   if (this.mSessionRefreshTimer) { this.mSessionRefreshTimer.cancel(); }
  882.   this.acUtils.removeCookies(this.webDetective.getSessionCookies("photobucket"));
  883. }
  884.  
  885.  
  886. photobucketService.prototype.getAuthUser =
  887. function photobucketService_getAuthUser()
  888. {
  889.   return this.api.mAuthUser;
  890. }
  891.  
  892. photobucketService.prototype.getAuthPerson =
  893. function photobucketService_getAuthPerson()
  894. {
  895.   var user = this.getAuthUser();
  896.   if (!user) {
  897.     return null;
  898.   }
  899.   var person = {};
  900.   person.id = user.id;
  901.   person.fullname = user.fullname;
  902.   person.username = user.username;
  903.   person.service = this;
  904.   return person;
  905. }
  906.  
  907.  
  908. photobucketService.prototype.notify = function photobucketService_sessionTimerNotify(aTimer) {
  909.   var inst = this;
  910.   var sessionRefreshListener = {
  911.     onAuth: function() {
  912.       inst.logger.debug('yay! pbucket refreshed its session key with the timer');
  913.     },
  914.  
  915.     onError: function(aError) {
  916.       inst.logger.error('!!!boo! error refreshing session key on the timer');
  917.     }
  918.   }
  919.   this.getSessionKey(sessionRefreshListener);
  920. }
  921.  
  922.  
  923. photobucketService.prototype.getSessionKey =
  924. function photobucketService_getSessionKey(aListener)
  925. {
  926.   var acctURN = this.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  927.   if (!acctURN) aListener.onError(null);
  928.   var token = this.faves_coop.get(acctURN).token;
  929.   this.logger.debug(">>>getSessionKey called with the token "+token+"\n\n\n");
  930.  
  931.   var inst = this;
  932.   var mySessionListener = {
  933.     onResult: function(aXML) {
  934.       var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  935.       var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
  936.       inst.api.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
  937.       inst.getAuthUser().id = username;
  938.       inst.getAuthUser().username = username;
  939.       inst.getAuthUser().fullname = username;
  940.       inst.getAuthUser().sessionkey = sessionkey;
  941.       aListener.onAuth();
  942.     },
  943.     onError: function(aErr) {
  944.       inst.logger.debug('>>>>>> ' + aErr.errorString + '\n\n')
  945.       aListener.onError(aErr);
  946.     }
  947.   };
  948.   var params = {};
  949.   params.service_token = token;
  950.   inst.call(mySessionListener, "getsession", params2Dictionary(params));
  951. }
  952.  
  953. photobucketService.prototype.upload =
  954. function photobucketService_upload(aListener, aUpload, aFile)
  955. {
  956.   var inst = this;
  957.   var sigParams = {};
  958.   var myParams = {};
  959.  
  960.   this.logger.debug('uploading to album ' + aUpload.album);
  961.   myParams.version = PHOTOBUCKET_API_VERSION;
  962.   myParams.scid = PHOTOBUCKET_SCID;
  963.   myParams.method = 'upload';
  964.   myParams.description = aUpload.description;
  965.   myParams.title = aUpload.title;
  966.   myParams.album_name = aUpload.album;
  967.   myParams.session_key = this.getAuthUser().sessionkey;
  968.   myParams.sig = this.api.buildSig("upload", sigParams, true);
  969.  
  970.   sigParams.session_key = this.getAuthUser().sessionkey;
  971.  
  972.   this.mUploader = new PhotoUploader();
  973.   this.mUploader.setEndpoint(inst.api.mApiUrl);
  974.   this.mUploader.photoParam = "uploadfile";
  975.  
  976.   var myListener = {
  977.     onResult: function(aXML) {
  978.       var photo = aXML.getElementsByTagName("photo")[0];
  979.       if (photo) {
  980.         inst.logger.debug('uploaded the pic named ' + photo.getAttribute('name'));
  981.         var rval = inst.handlePhotosResult(aXML);
  982.         aListener.onUploadComplete(aUpload);
  983.         aListener.onUploadFinalized(aUpload, rval[0]);
  984.       }
  985.       else {
  986.         aListener.onError(inst.api.getError("SERVICE_ERROR", aXML, null));
  987.       }
  988.     },
  989.     onError: function(aErrorCode) {
  990.       if (aErrorCode) {
  991.         aListener.onError(inst.api.getError("HTTP_ERROR", null, aErrorCode));
  992.       } else {
  993.         aListener.onError(inst.api.getError("SERVICE_ERROR", null, null));
  994.       }
  995.     },
  996.     onProgress: function(aCurrentProgress) {
  997.         aListener.onProgress(aCurrentProgress);
  998.     }
  999.   }
  1000.   this.mUploader.upload(myListener, aFile, myParams, aUpload);
  1001. }
  1002.  
  1003. photobucketService.prototype.cancelUpload =
  1004. function photobucketService_cancelUpload()
  1005. {
  1006.   try {
  1007.     this.mUploader.req.abort();
  1008.   } catch(e) { }
  1009. }
  1010.  
  1011.  
  1012. // flockIPollingService implementation
  1013.  
  1014. photobucketService.prototype.refresh =
  1015. function photobucketService_refresh(aURN, aListener)
  1016. {
  1017.   var refreshItem = this.faves_coop.get(aURN);
  1018.   if (refreshItem.isInstanceOf(this.faves_coop.Account)) {
  1019.     aListener.onResult();
  1020.   }
  1021. }
  1022.  
  1023.  
  1024. photobucketService.prototype.migrateAccount =
  1025. function photobucketService_migrateAccount( aId, aUsername) {
  1026.   this.init();
  1027.  
  1028.   var token = '';
  1029.   try {
  1030.     token = flock_getCharPref(PHOTOBUCKET_TOKEN_PREF);
  1031.   } catch (e) { }
  1032.  
  1033.   this.addAccountById(aId, false, null, aUsername, token);
  1034. }
  1035.  
  1036. // BEGIN flockIWebService interface
  1037. photobucketService.prototype.addAccountById =
  1038. function photobucketService_addAccountById(aAccountID, aIsTransient, aListener, aUsername, aToken)
  1039. {
  1040.   this.logger.debug("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)");
  1041.  
  1042.   if (!aUsername) {
  1043.     // Get the password associated with this account
  1044.     var pw = this.acUtils.getPassword(this.urn+':'+aAccountID);
  1045.     var name = pw.user;
  1046.     var token = '';
  1047.     var pollable = false;
  1048.     var auth = false;
  1049.   } else {
  1050.     var name = aUsername;
  1051.     var token = aToken;
  1052.     var pollable = true;
  1053.     var auth = true;
  1054.   }
  1055.  
  1056.   var accountURN = "urn:flock:photobucket:"+aAccountID;
  1057.   var acctCoopObj = this.faves_coop.get(accountURN);
  1058.   if (!acctCoopObj) {
  1059.     acctCoopObj = new this.faves_coop.Account(
  1060.       accountURN,
  1061.       {
  1062.         accountId: aAccountID,
  1063.         name: name, // This gets changed to the user's full name once logged in
  1064.         serviceId: PHOTOBUCKET_CONTRACTID,
  1065.         service: this.pbService,
  1066.         URL: "http://www.photobucket.com/",
  1067.         favicon: this.icon,
  1068.         isTransient: aIsTransient,
  1069.         isPollable: pollable,
  1070.         authToken: token,
  1071.         isAuthenticated: auth
  1072.       }
  1073.     );
  1074.     this.faves_coop.accounts_root.children.add(acctCoopObj);
  1075.   }
  1076.  
  1077.   // perhaps we should do a checktoken and if valid go ahead and set the account as logged in?
  1078.  
  1079.   var acct = this.getAccount(acctCoopObj.id());
  1080.   if (aListener) aListener.onSuccess(acct, "addAccount");
  1081.   return acct;
  1082. }
  1083. // END flockIWebService interface
  1084.  
  1085.  
  1086. // BEGIN flockIAuthenticateNewAccount interface
  1087. photobucketService.prototype.authenticateNewAccount =
  1088. function photobucketService_authenticateNewAccount(aListener)
  1089. {
  1090.   this.logger.debug("{flockIAuthenticateNewAccount}.authenticateNewAccount(aListener)");
  1091.   aListener.onStart(null, "newaccountstarted");
  1092.   var inst = this;
  1093.   var tokenListener = {
  1094.     onResult: function (aXML) {
  1095.       inst.logger.debug(".authenticateNewAccount(): tokenListener: onResult()");
  1096.       var params = {
  1097.         service_token: aXML.getElementsByTagName("service_token")[0].firstChild.nodeValue
  1098.       };
  1099.       var sessionListener = {
  1100.         onResult: function (aXML) {
  1101.           inst.logger.debug(".authenticateNewAccount(): tokenListener: sessionListener: onResult()");
  1102.           var username = aXML.getElementsByTagName("username")[0].firstChild.nodeValue;
  1103.           var sessionkey = aXML.getElementsByTagName("session_key")[0].firstChild.nodeValue;
  1104.           inst.api.mApiUrl = aXML.getElementsByTagName("url")[0].firstChild.nodeValue;
  1105.           inst.getAuthUser().id = username;
  1106.           inst.getAuthUser().username = username;
  1107.           inst.getAuthUser().fullname = username;
  1108.           inst.getAuthUser().sessionkey = sessionkey;
  1109.           var acctURN = inst.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  1110.           var account = null;
  1111.           try {
  1112.             account = inst.getAccount(acctURN);
  1113.             if (!account) throw "ACCOUNT WAS NOT CREATED";
  1114.           } catch (ex) {
  1115.             dump(ex);
  1116.             inst.logger.error(".authenticateNewAccount(): tokenListener: onResult(): ERROR: account was not created");
  1117.             if (aListener) {
  1118.               aListener.onError(null, "newaccount", null);
  1119.             }
  1120.             return;
  1121.           }
  1122.           if (aListener) {
  1123.             aListener.onSuccess(account, "newaccount");
  1124.           }
  1125.         },
  1126.         onError: function (aError) {
  1127.           inst.logger.debug(".authenticateNewAccount(): tokenListener: sessionListener: onError()");
  1128.           inst.logger.debug(">>>>>> " + aError.errorString);
  1129.           if (aListener) {
  1130.             aListener.onError(aError, "newaccount", aError);
  1131.           }
  1132.         }
  1133.       };
  1134.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1135.                          .getService(Components.interfaces.nsIWindowMediator);
  1136.       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1137.                          .getService(Components.interfaces.nsIWindowWatcher);
  1138.       var topWin = wm.getMostRecentWindow(null);
  1139.       var loginUrl = inst.api.buildUrl(gStrings["serviceloginURL"], "login", params);
  1140.       var url = "chrome://browser/content/flock/photo/photoLoginWindow.xul?"
  1141.               + "url="+escape(loginUrl)
  1142.               + "&finalString="+escape("You have granted");
  1143.       var chrome = "chrome,close,titlebar,resizable=yes,toolbar,dialog=no,"
  1144.                  + "scrollbars=yes,modal,centerscreen";
  1145.       topWin.open(url, "_blank", chrome);
  1146.       // Wait till the window closes
  1147.       inst.api.call(sessionListener, "getsession", params2Dictionary(params));
  1148.     },
  1149.     onError: function (aError) {
  1150.       inst.logger.debug(".authenticateNewAccount(): tokenListener: onError()");
  1151.       inst.logger.debug(">>>>>> " + aError.errorString);
  1152.       if (aListener) {
  1153.         aListener.onError(aError, "newaccount");
  1154.       }
  1155.     }
  1156.   };
  1157.   if (!this.api.mAuthUser) {
  1158.     this.api.mAuthUser = {};
  1159.   }
  1160.   this.api.getToken(tokenListener);
  1161. }
  1162. // END flockIAuthenticateNewAccount interface
  1163.  
  1164.  
  1165. // BEGIN flockIManageableWebService interface
  1166. photobucketService.prototype.getAccountIDFromDocument =
  1167. function photobucketService_getAccountIDFromDocument(aDocument)
  1168. {
  1169.   this.logger.debug("{flockIManageableWebService}.getAccountIDFromDocument(aDocument)");
  1170.   aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
  1171.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1172.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1173.   if (this.webDetective.detect("photobucket", "accountinfo", aDocument, results)) {
  1174.     return results.getPropertyAsAString("userid");
  1175.   } else {
  1176.     // We could not detect any account info on the login landing page.  This
  1177.     // may mean that it's the API authentication login landing page, which
  1178.     // doesn't have any account info.  If this is the case, then we should
  1179.     // have a temp password entry associated with the session cookie.
  1180.     var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1181.     var pw = this.acUtils.getTempPassword("photobucket:session:"+sessionValue);
  1182.     if (pw) {
  1183.       this.logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  1184.       return pw.user;
  1185.     }
  1186.   }
  1187.   return null;
  1188. }
  1189.  
  1190. photobucketService.prototype.getCredentialsFromForm =
  1191. function photobucketService_getCredentialsFromForm(aForm)
  1192. {
  1193.   var inst = this;
  1194.   var detectForm = function (aType, aResults) {
  1195.     return inst.webDetective.detectForm("photobucket", aType, aForm, aResults);
  1196.   };
  1197.  
  1198.   var formType = "login";
  1199.   var results = getCompTK().newResults();
  1200.   if (!detectForm(formType, results)) {
  1201.     formType = "signup";
  1202.     results = getCompTK().newResults();
  1203.     if (!detectForm(formType, results)) {
  1204.       formType = "changepassword";
  1205.       results = getCompTK().newResults();
  1206.       if (!detectForm(formType, results)) {
  1207.         results = null;
  1208.       }
  1209.     }
  1210.   }
  1211.  
  1212.   if (results) {
  1213.     var pw = {
  1214.       QueryInterface: function(aIID) {
  1215.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  1216.             !aIID.equals(Components.interfaces.nsIPassword) &&
  1217.             !aIID.equals(Components.interfaces.flockIPassword)) { 
  1218.           throw Components.interfaces.NS_ERROR_NO_INTERFACE;
  1219.         }
  1220.         return this;
  1221.       },
  1222.       user: results.getPropertyAsAString("username"),
  1223.       password: results.getPropertyAsAString("password"),
  1224.       host: null,
  1225.       formType: formType
  1226.     };
  1227.  
  1228.     // Doing a bit of a hack here...
  1229.     // Since the Photobucket login landing page doesn't always reveal which
  1230.     // account is logged in, we will need to look at the session cookie and
  1231.     // see if its the same as what it was when the user last logged in.  So
  1232.     // at this point we're just storing the account username as a temp
  1233.     // password entry associated with the session cookie token.
  1234.     var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1235.     this.acUtils.clearTempPassword("photobucket:session:"+sessionValue);
  1236.     this.acUtils.setTempPassword("photobucket:session:"+sessionValue, pw.user, "", formType);
  1237.     return pw;
  1238.   }
  1239.   return null;
  1240. }
  1241.  
  1242. photobucketService.prototype.updateAccountStatusFromDocument =
  1243. function photobucketService_updateAccountStatusFromDocument(aDocument)
  1244. {
  1245.   var inst = this;
  1246.   this.logger.debug("{flockIManageableWebService}.updateAccountStatusFromDocument('"+aDocument.URL+"')");
  1247.   if (this.webDetective.detect("photobucket", "loggedout", aDocument, null)) {
  1248.     this.acUtils.markAllAccountsAsLoggedOut(PHOTOBUCKET_CONTRACTID);
  1249.     this.logout();
  1250.   } else if (this.webDetective.detect("photobucket", "loggedin", aDocument, null)) {
  1251.     var acctID;
  1252.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1253.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1254.     if (this.webDetective.detect("photobucket", "accountinfo", aDocument, results)) {
  1255.       acctID = results.getPropertyAsAString("userid");
  1256.     } else {
  1257.       // We could not detect any account info on the login landing page.  This
  1258.       // may mean that it's the API authentication login landing page, which
  1259.       // doesn't have any account info.  If this is the case, then we should
  1260.       // have a temp password entry associated with the session cookie.
  1261.       var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1262.       var pw = this.acUtils.getTempPassword("photobucket:session:"+sessionValue);
  1263.       if (pw) {
  1264.         this.logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  1265.         acctID = pw.user;
  1266.       }
  1267.     }
  1268.     if (acctID) {
  1269.       var acctURN = this.acUtils.getAccountURNById(this.urn, acctID);
  1270.       if (acctURN) {
  1271.         var acctCoopObj = this.faves_coop.get(acctURN);
  1272.         if (!acctCoopObj.isAuthenticated) {
  1273.           // Re-authenticate the API
  1274.           var reauthListener = {
  1275.             onAuth: function () {
  1276.               inst.logger.debug("reauthListener: onAuth()");
  1277.             },
  1278.             onError: function (aError) {
  1279.               inst.logger.debug("reauthListener: onError()");
  1280.               acctCoopObj.isAuthenticated = false;
  1281.             }
  1282.           };
  1283.           this.getAccount(acctURN).login(reauthListener);
  1284.           // Prematurely set isAuthenticated to TRUE in order to avoid other
  1285.           // asyncronous auth attempts before this one completes.
  1286.           acctCoopObj.isAuthenticated = true;
  1287.         }
  1288.       }
  1289.     }
  1290.   }
  1291. }
  1292. // END flockIManageableWebService interface
  1293.  
  1294.  
  1295. // BEGIN flockIMediaWebService interface
  1296. photobucketService.prototype.decorateForMedia =
  1297. function photobucketService_decorateForMedia(aDocument)
  1298. {
  1299.   this.logger.debug("{flockIMediaWebService}.decorateForMedia(aDocument)");
  1300.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1301.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1302.   if (this.webDetective.detect("photobucket", "media", aDocument, results)) {
  1303.     var userid = results.getPropertyAsAString("userid");
  1304.     var mediaArr = new Array();
  1305.  
  1306.     if (!aDocument._flock_decorations) {
  1307.       aDocument._flock_decorations = new Object();
  1308.     }
  1309.     var media = {
  1310.       name: userid,
  1311.       query: 'user:' + userid + "|username:" + userid,
  1312.       label: userid,
  1313.       favicon: this.icon,
  1314.       service: this.shortName
  1315.     }
  1316.     mediaArr.push(media);
  1317.  
  1318.     aDocument._flock_decorations.mediaArr = mediaArr;
  1319.     this.obs.notifyObservers(aDocument, 'media', 'media:update');
  1320.   }
  1321. }
  1322.  
  1323. photobucketService.prototype.handlesMediaStream =
  1324. function photobucketService_handlesMediaStream() 
  1325. {
  1326.   return true;
  1327. }
  1328.  
  1329. photobucketService.prototype.checkIsStreamUrl =
  1330. function photobucketService_checkIsStreamUrl(aUrl)
  1331. {
  1332.   // Videos:
  1333.   // /player.swf?file=http://vid166.photobucket.com/albums/u113/h671226/Smart_Dog.flv
  1334.   // Images:
  1335.   // http://i166.photobucket.com/albums/u105/such-a-craka/thfhg.jpg
  1336.   if (this.webDetective.detectNoDOM("photobucket", "isStreamUrl", "", aUrl, null)) {
  1337.     this.logger.debug("Checking if url is photobucket stream: YES: " + aUrl);
  1338.     return true;
  1339.   }
  1340.   this.logger.debug("Checking if url is photobucket stream: NO: " + aUrl);
  1341.   return false;
  1342. }
  1343.  
  1344. photobucketService.prototype.getMediaQueryFromURL =
  1345. function photobucketService_getMediaQueryFromURL(aUrl, aListener)
  1346. {
  1347.   var userName = null;
  1348.   var detectResults = Cc["@mozilla.org/hash-property-bag;1"]
  1349.                       .createInstance(Ci.nsIWritablePropertyBag2);
  1350.   if (this.webDetective.detectNoDOM("photobucket", "person", "", aUrl, detectResults)) {
  1351.     userName = detectResults.getPropertyAsAString("userid");
  1352.   }
  1353.  
  1354.   if (userName) {
  1355.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1356.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1357.     results.setPropertyAsAString("query", "user:" + userName + "|username:" + userName);
  1358.     results.setPropertyAsAString("title", this.title + " user: " + userName);
  1359.     aListener.onSuccess(results, "query");
  1360.   } else {
  1361.     aListener.onError(null, "Unable to get user.", null);
  1362.   }
  1363. }
  1364. // END flockIMediaWebService interface
  1365.  
  1366.  
  1367. photobucketService.prototype.doMigration =
  1368. function photobucketService_doMigration(aDatasource, aAccountURN) {
  1369. }
  1370.  
  1371.  
  1372. // ================================================
  1373. // ========== BEGIN XPCOM Module support ==========
  1374. // ================================================
  1375.  
  1376. function createModule(aParams) {
  1377.   return {
  1378.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  1379.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1380.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1381.                                         aParams.contractID, aFileSpec,
  1382.                                         aLocation, aType );
  1383.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  1384.         .getService(Ci.nsICategoryManager);
  1385.       if (!aParams.categories) { aParams.categories = []; }
  1386.       for (var i = 0; i < aParams.categories.length; i++) {
  1387.         var cat = aParams.categories[i];
  1388.         catMgr.addCategoryEntry( cat.category, cat.entry,
  1389.                                  cat.value, true, true );
  1390.       }
  1391.     },
  1392.     getClassObject: function (aCompMgr, aCID, aIID) {
  1393.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  1394.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  1395.       return { // Factory
  1396.         createInstance: function (aOuter, aIID) {
  1397.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  1398.           var comp = new aParams.componentClass();
  1399.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  1400.           return comp.QueryInterface(aIID);
  1401.         }
  1402.       };
  1403.     },
  1404.     canUnload: function (aCompMgr) { return true; }
  1405.   };
  1406. }
  1407.  
  1408. // NS Module entrypoint
  1409. function NSGetModule(aCompMgr, aFileSpec) {
  1410.   return createModule({
  1411.     componentClass: photobucketService,
  1412.     CID: PHOTOBUCKET_CID,
  1413.     contractID: PHOTOBUCKET_CONTRACTID,
  1414.     componentName: CATEGORY_COMPONENT_NAME,
  1415.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  1416.     categories: [
  1417.       { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: PHOTOBUCKET_CONTRACTID },
  1418.       { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: PHOTOBUCKET_CONTRACTID },
  1419.       { category: "flockMediaProvider", entry: CATEGORY_ENTRY_NAME, value: PHOTOBUCKET_CONTRACTID }
  1420.     ]
  1421.   });
  1422. }
  1423.  
  1424. // ========== END XPCOM Module support ==========
  1425.  
  1426.  
  1427. // ====================================================
  1428. // ============= BEGIN photobucket API ================
  1429. // ====================================================
  1430.  
  1431. function PhotobucketAPI() {
  1432.   this._logger = Cc["@flock.com/logger;1"]
  1433.                  .createInstance(Ci.flockILogger);
  1434.   this._logger.init("photobucketAPI");
  1435.   this._logger.info("Created Photobucket API Object");
  1436.   this.acUtils = Cc["@flock.com/account-utils;1"]
  1437.                  .getService(Ci.flockIAccountUtils);
  1438.   this.webDetective = this.acUtils.useWebDetective("photobucket.xml");
  1439.   this.faves_coop = Cc["@flock.com/singleton;1"]
  1440.                     .getService(Ci.flockISingleton)
  1441.                     .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  1442.                     .wrappedJSObject;
  1443.  
  1444.   this.mApiUrl = gStrings["apiURL"];
  1445.   this.mAuthUser = null;
  1446.   this.mSessionRefreshTimer = null;
  1447.   var inst = this;
  1448. };
  1449.  
  1450.  
  1451. PhotobucketAPI.prototype.isLoggedIn =
  1452. function photobucketAPI_isLoggedIn() {
  1453.   return (this.mAuthUser && this.mAuthUser.sessionkey != null);
  1454. };
  1455.  
  1456.  
  1457. PhotobucketAPI.prototype.getToken =
  1458. function photobucketAPI_getToken(aListener) {
  1459.   var inst = this;
  1460.   var tokenListener = {
  1461.     onResult: function (aXML) {
  1462.       try {
  1463.         var resp = aXML.getElementsByTagName("response");
  1464.         if (resp[0].getAttribute("stat") == "ok") {
  1465.           //rval = resp[0].firstChild.nodeValue;
  1466.           aListener.onResult(aXML);
  1467.         } else {
  1468.           aListener.onError(null);  //TODO - figure out errors
  1469.           //rval = -1;
  1470.         }
  1471.       }
  1472.       catch(e) {
  1473.         inst._logger.error(e);
  1474.       }
  1475.     },
  1476.     onError: function (aError) {
  1477.       inst._logger.error(aError.errorString);
  1478.       aListener.onError(aError);
  1479.     }
  1480.   }
  1481.   var params = {};
  1482.   this.call(tokenListener, "getservicetoken", params2Dictionary(params));
  1483. };
  1484.  
  1485.  
  1486. PhotobucketAPI.prototype.call =
  1487. function photobucketAPI_call(aListener,
  1488.                              aMethod,
  1489.                              aDictionary,
  1490.                              aAuth) {
  1491.   var inst = this;
  1492.   var params = this.dictionary2Params(aDictionary);
  1493.   var url = this.buildUrl(inst.mApiUrl, aMethod, params, aAuth);
  1494.   this._logger.debug(url + " < call");
  1495.   this.req = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
  1496.              .createInstance(Ci.nsIXMLHttpRequest)
  1497.              .QueryInterface(Ci.nsIJSXMLHttpRequest);
  1498.  
  1499.   this.req.open("GET", url, true);
  1500.   var req = this.req;
  1501.   this.req.onreadystatechange = function (aEvt) {
  1502.     if (req.readyState == 4) {
  1503.       try {
  1504.         inst._logger.debug("doCall[onreadystatechange] req.status = "
  1505.                            + req.status);
  1506.         if (req.status/100 == 2) {
  1507.           inst._logger.debug(req.responseText);
  1508.           var rsp = req.responseXML.getElementsByTagName("response")[0];
  1509.           var stat = rsp.getAttribute("stat");
  1510.           if (stat != "ok") {
  1511.             aListener.onError(inst.getError("SERVICE_ERROR",
  1512.                                             req.responseXML,
  1513.                                             null));
  1514.           } else {
  1515.             aListener.onResult(req.responseXML);
  1516.           }
  1517.         } else {
  1518.           aListener.onError(inst.getError("HTTP_ERROR", null, req.status));
  1519.         }
  1520.       } catch(e) {
  1521.         inst._logger.error(e);
  1522.         // We need to convert to a string because that's what getError expects
  1523.         var errorCode = String(Ci.flockIError.XML_HTTP_REQUEST_ERROR)
  1524.         aListener.onError(inst.getError("HTTP_ERROR", null, errorCode));
  1525.       }
  1526.     }
  1527.   }
  1528.   this.req.send(null);
  1529. };
  1530.  
  1531.  
  1532. PhotobucketAPI.prototype.dictionary2Params =
  1533. function photobucketAPI_dictionary2Params(aDictionary) {
  1534.   var params = {};
  1535.   var obj = {};
  1536.   var count = {};
  1537.   var keys = aDictionary.getKeys(count, obj);
  1538.   for (var i = 0; i < keys.length; ++i) {
  1539.     var supports = aDictionary.getValue(keys[i]);
  1540.     var supportsString = supports.QueryInterface(Ci.nsISupportsString);
  1541.     var val = supportsString.toString();
  1542.     params[keys[i]] = val;
  1543.   }
  1544.   return params;
  1545. };
  1546.  
  1547. PhotobucketAPI.prototype.authenticatedCall =
  1548. function photobucketAPI_authenticatedCall(aListener,
  1549.                                           aMethod,
  1550.                                           aDictionary) {
  1551.   this._logger.debug("authenticatedCall " + aMethod);
  1552.   if (!this.isLoggedIn()) {
  1553.     this._logger.debug("not logged in, so trying unauth call for " + aMethod);
  1554.     return this.call(aListener, aMethod, aDictionary);
  1555.   }
  1556.   this._logger.debug("+++++" + this.mAuthUser.sessionkey);
  1557.   return this.call(aListener, aMethod, aDictionary, true);
  1558. };
  1559.  
  1560. PhotobucketAPI.prototype.doLogin =
  1561. function photobucketAPI_doLogin(aAccountURN, aListener) {
  1562.   this._logger.debug(".doLogin('" + aAccountURN + "')");
  1563.   var inst = this;
  1564.   var tokenListener = {
  1565.     onResult: function(aXML) {
  1566.       inst._logger.debug(".doLogin('"
  1567.                          + aAccountURN +
  1568.                          "'): tokenListener: onResult()");
  1569.       // Time to authenticate...
  1570.       var params = {
  1571.         service_token: aXML.getElementsByTagName("service_token")[0]
  1572.                            .firstChild
  1573.                            .nodeValue
  1574.       };
  1575.       // persist the token
  1576.       var acctCoopObj = inst.faves_coop.get(aAccountURN);
  1577.       acctCoopObj.authToken = params.service_token;
  1578.  
  1579.       // listener for the upcoming "getsession" api call
  1580.       var sessionListener = {
  1581.         onResult: function(aXML) {
  1582.           inst._logger.debug(".doLogin('"
  1583.                              + aAccountURN
  1584.                              + "'): tokenListener:"
  1585.                              + "sessionListener: onResult()");
  1586.           var username = aXML.getElementsByTagName('username')[0]
  1587.                              .firstChild
  1588.                              .nodeValue;
  1589.           var sessionkey = aXML.getElementsByTagName('session_key')[0]
  1590.                                .firstChild
  1591.                                .nodeValue;
  1592.           inst.mApiUrl = aXML.getElementsByTagName('url')[0]
  1593.                              .firstChild
  1594.                              .nodeValue;
  1595.           inst.mAuthUser.id = username;
  1596.           inst.mAuthUser.username = username;
  1597.           inst.mAuthUser.fullname = username;
  1598.           inst.mAuthUser.sessionkey = sessionkey;
  1599.           inst.acUtils.ensureOnlyAuthenticatedAccount(aAccountURN);
  1600.           if (!inst.mSessionRefreshTimer) inst.fireSessionRefreshTimer();
  1601.           aListener.onSuccess(null, "");
  1602.         },
  1603.         onError: function(aErr) {
  1604.           inst._logger.error(".doLogin('"
  1605.                              + aAccountURN
  1606.                              + "'): tokenListener: sessionListener: onError()");
  1607.           inst._logger.error(">>>>>> " + aErr.errorString);
  1608.           aListener.onError(aErr);
  1609.         }
  1610.       };
  1611.  
  1612.       // If we have the user's un/pw, then we can do this silently...
  1613.       var pw = inst.acUtils.getPassword("urn:photobucket:service:"
  1614.                                         + acctCoopObj.accountId);
  1615.       if (pw) {
  1616.         inst._logger.debug(".doLogin('"
  1617.                            + aAccountURN
  1618.                            + "'): tokenListener: onResult(): found password");
  1619.  
  1620.         var postBody = "action=login"
  1621.                      + "&service_token="+params.service_token
  1622.                      + "&sig="+inst.buildSig("login", params)
  1623.                      + "&scid="+PHOTOBUCKET_SCID
  1624.                      + "&method=login"
  1625.                      + "&callback_verify="
  1626.                      + "&username="+pw.user
  1627.                      + "&password="+pw.password
  1628.                      + "&login=Login";
  1629.         var hr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
  1630.                  .createInstance(Ci.nsIXMLHttpRequest)
  1631.                  .QueryInterface(Ci.nsIJSXMLHttpRequest);
  1632.  
  1633.         var onReadyStateFunc = function(eEvt) {
  1634.           if (hr.readyState == 4) {
  1635.             inst._logger.debug(".doLogin('"
  1636.                                + aAccountURN
  1637.                                + "'): tokenListener:"
  1638.                                + "onResult(): hr.onreadystatechange()");
  1639.             var results = Cc["@mozilla.org/hash-property-bag;1"]
  1640.                           .createInstance(Ci.nsIWritablePropertyBag2);
  1641.             if (inst.webDetective.detectNoDOM("photobucket",
  1642.                                               "apiauth",
  1643.                                               "",
  1644.                                               hr.responseText,
  1645.                                               results)) {
  1646.               var hr2 = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
  1647.                         .createInstance(Ci.nsIXMLHttpRequest)
  1648.                         .QueryInterface(Ci.nsIJSXMLHttpRequest);
  1649.  
  1650.               hr2.onreadystatechange = function(eEvt2) {
  1651.                 if (hr2.readyState == 4) {
  1652.                   inst.call(sessionListener,
  1653.                             "getsession",
  1654.                             params2Dictionary(params));
  1655.                 }
  1656.               }
  1657.  
  1658.               var postBody2 = "action=complete"
  1659.                             + "&authorized=yes"
  1660.                             + "&service_token=" + params.service_token
  1661.                             + "&sig=" + inst.buildSig("login", params)
  1662.                             + "&scid=" + PHOTOBUCKET_SCID
  1663.                             + "&method=login"
  1664.                             + "&callback_verify="
  1665.                             + "&user_id="
  1666.                             + results.getPropertyAsAString("user_id");
  1667.  
  1668.               hr2.backgroundRequest = true;
  1669.               hr2.open("POST", gStrings["serviceloginURL"],true);
  1670.               hr2.setRequestHeader("Content-Type",
  1671.                                    "application/x-www-form-urlencoded");
  1672.               hr2.send(postBody2);
  1673.             } else {
  1674.               inst.call(sessionListener,
  1675.                         "getsession",
  1676.                         params2Dictionary(params));
  1677.             }
  1678.           }
  1679.         };
  1680.  
  1681.         hr.onreadystatechange = onReadyStateFunc;
  1682.         hr.backgroundRequest = true;
  1683.         hr.open("POST", gStrings["serviceloginURL"],true);
  1684.         hr.setRequestHeader("Content-Type",
  1685.                             "application/x-www-form-urlencoded");
  1686.         hr.send(postBody);
  1687.  
  1688.       } else {
  1689.         inst._logger.debug(".doLogin('"
  1690.                            + aAccountURN +
  1691.                            "'): tokenListener: onResult(): NO password!");
  1692.  
  1693.         // No password stored, so we have to prompt the user
  1694.         var windowManager = Cc["@mozilla.org/appshell/window-mediator;1"]
  1695.                             .getService(Ci.nsIWindowMediator);
  1696.         var topWindow = windowManager.getMostRecentWindow(null);
  1697.  
  1698.         var loginUrl = inst.buildUrl(gStrings["serviceloginURL"],
  1699.                                      "login",
  1700.                                      params);
  1701.         var url = "chrome://browser/content/flock/photo/photoLoginWindow.xul?"
  1702.                 + "url=" + escape(loginUrl)
  1703.                 + "&finalString=" + escape("You have granted");
  1704.         var options = "chrome,"
  1705.                     + "close,"
  1706.                     + "titlebar,"
  1707.                     + "resizable=yes,"
  1708.                     + "toolbar,"
  1709.                     + "dialog=no,"
  1710.                     + "scrollbars=yes,"
  1711.                     + "modal,"
  1712.                     + "centerscreen";
  1713.         topWindow.open(url, "_blank", options);
  1714.         // we are waiting till window closes
  1715.  
  1716.         inst.call(sessionListener, "getsession", params2Dictionary(params));
  1717.       }
  1718.     },
  1719.     onError: function(aErr) {
  1720.       inst._logger.error(".doLogin('"
  1721.                          + aAccountURN
  1722.                          + "'): tokenListener: onError()");
  1723.       aListener.onError(aErr);
  1724.     }
  1725.   }
  1726.  
  1727.   if (!this.mAuthUser) {
  1728.     this.mAuthUser = {};
  1729.   }
  1730.   this.getToken(tokenListener);
  1731. };
  1732.  
  1733. PhotobucketAPI.prototype.fireSessionRefreshTimer =
  1734. function photobucketAPI_fireSessionRefreshTimer() {
  1735.   this.mSessionRefreshTimer = Cc["@mozilla.org/timer;1"]
  1736.                               .createInstance(Ci.nsITimer);
  1737.   // refresh the session key once an hour, probably can adjust this
  1738.   this.mSessionRefreshTimer.initWithCallback(this,
  1739.                                              PHOTOBUCKET_SESSION_REFRESH_INTERVAL,
  1740.                                              2);
  1741. };
  1742.  
  1743. PhotobucketAPI.prototype.getError =
  1744. function photobucketAPI_getError(aErrorType, aXML, aHTTPErrorCode) {
  1745.   var errorNode;
  1746.   var code;
  1747.   var serverErrorMessage;
  1748.   var error = Cc[FLOCK_ERROR_CONTRACTID]
  1749.               .createInstance(flockIError);
  1750.  
  1751.   this._logger.debug("getError aErrorType="
  1752.                      + aErrorType
  1753.                      + "; aHTTPErrorCode="
  1754.                      + aHTTPErrorCode);
  1755.  
  1756.   if (aErrorType == "HTTP_ERROR") {
  1757.     error.errorCode = aHTTPErrorCode;
  1758.     return error;
  1759.   } else if (aErrorType == "SERVICE_ERROR") {
  1760.     try {
  1761.       errorNode = aXML.getElementsByTagName("error")[0];
  1762.       code = errorNode.getAttribute("code");
  1763.       serverErrorMessage = errorNode.getAttribute("msg");
  1764.       error.serviceErrorCode = code;
  1765.       error.serviceErrorString = serverErrorMessage;
  1766.       this._logger.debug("Found error code = "
  1767.                          + code
  1768.                          + " and message = '"
  1769.                          + serverErrorMessage
  1770.                          + "'");
  1771.     } catch (ex) {
  1772.       code = "-1"
  1773.     }
  1774.  
  1775.     if (!code) code = "-1";
  1776.  
  1777.     switch (code) {
  1778.       case "-1":
  1779.         error.errorCode = error.PHOTOSERVICE_INVALID_RESPONSE;
  1780.         break;
  1781.       case "007":
  1782.       case "008":
  1783.         error.errorCode = error.PHOTOSERVICE_LOGIN_FAILED;
  1784.         break;
  1785.       case "012":
  1786.         error.errorCode = error.PHOTOSERVICE_SESSION_KEY_EXPIRED;
  1787.         break;
  1788.       case "101":
  1789.         error.errorCode = error.PHOTOSERVICE_EMPTY_ALBUMNAME;
  1790.         break;
  1791.       case "102":
  1792.         error.errorCode = error.PHOTOSERVICE_INVALID_USER;
  1793.         break;
  1794.       case "103":
  1795.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1796.         break;
  1797.       case "104":
  1798.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1799.         break;
  1800.       case "105":
  1801.         error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
  1802.         break;
  1803.       case "106":
  1804.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1805.         break;
  1806.       case "107":
  1807.         error.errorCode = error.PHOTOSERVICE_INVALID_ALBUM;
  1808.         break;
  1809.       case "108":
  1810.         error.errorCode = error.PHOTOSERVICE_PRIVATE_ALBUM;
  1811.         break;
  1812.       case "109":
  1813.         error.errorCode = error.PHOTOSERVICE_PHOTOS_IN_ALBUM_LIMIT_REACHED;
  1814.         break;
  1815.       case "110":
  1816.         error.errorCode = error.PHOTOSERVICE_NO_FILE_UPLOADED;
  1817.         break;
  1818.       case "111":
  1819.         error.errorCode = error.PHOTOSERVICE_ALBUM_IS_OVER_SIZE_LIMIT;
  1820.         break;
  1821.       case "112":
  1822.         error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
  1823.         break;
  1824.       case "113":
  1825.         error.errorCode = error.PHOTOSERVICE_DUPLICATE_FILENAME;
  1826.         break;
  1827.       case "115":
  1828.         error.errorCode = error.PHOTOSERVICE_INVALID_ALBUMNAME;
  1829.         break;
  1830.       case "116":
  1831.         error.errorCode = error.PHOTOSERVICE_DUPLICATE_ALBUMNAME;
  1832.         break;
  1833.       case "314":
  1834.         error.errorCode = error.PHOTOSERVICE_FILE_IS_OVER_SIZE_LIMIT;
  1835.         break;
  1836.       default:
  1837.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  1838.         if (serverErrorMessage && serverErrorMessage.length) {
  1839.           error.serviceErrorString = serverErrorMessage;
  1840.         }
  1841.         break;
  1842.     }
  1843.     return error;
  1844.   }
  1845.   return null;
  1846. };
  1847.  
  1848. PhotobucketAPI.prototype.buildUrl =
  1849. function photobucketAPI_buildUrl(aUrl, aMethod, aParams, aAuth) {
  1850.   var qs = "";
  1851.   var strConcat = "";
  1852.   for (var p in aParams) {
  1853.     qs += "&" + escape(p) + "=" + escape(aParams[p]);
  1854.     strConcat += escape(aParams[p]);
  1855.   }
  1856.   var rval = aUrl
  1857.            + "?method=" + aMethod
  1858.            + "&version=" + PHOTOBUCKET_API_VERSION;
  1859.   if (aAuth) {
  1860.     rval += "&session_key=" + this.mAuthUser.sessionkey;
  1861.   }
  1862.   rval += "&scid=" + PHOTOBUCKET_SCID
  1863.        + qs
  1864.        + "&sig=" + this.buildSig(aMethod, aParams, aAuth);
  1865.   this._logger.debug("this is your buildUrl: " + rval + "\n");
  1866.   return rval;
  1867. };
  1868.  
  1869. PhotobucketAPI.prototype.buildSig =
  1870. function photobucketAPI_buildSig(aMethod, aParams, aAuth) {
  1871.   var qs = "";
  1872.   var strConcat = "";
  1873.   for (var p in aParams) {
  1874.     qs += "&" + escape(p) + "=" + escape(aParams[p]);
  1875.     if (p == 'service_token') {
  1876.       strConcat += escape(aParams[p]);
  1877.     }
  1878.   }
  1879.  
  1880.   this._logger.debug(strConcat + "<<<<<<<<strconcat\n");
  1881.   var preHash = aMethod
  1882.                 + PHOTOBUCKET_PK
  1883.                 + PHOTOBUCKET_SCID
  1884.                 + strConcat
  1885.                 + (aAuth ? this.mAuthUser.sessionkey : "");
  1886.   this._logger.debug("going to sig::: " + preHash);
  1887.  
  1888.   var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  1889.                   .createInstance(Ci.nsIScriptableUnicodeConverter);
  1890.   converter.charset = "UTF-8";
  1891.  
  1892.   var inputStream = converter.convertToInputStream(preHash);
  1893.   return FlockCryptoHash.md5Stream(inputStream);
  1894. };
  1895.  
  1896. // ========== END PhotobucketAPI class ==========
  1897.  
  1898.  
  1899.  
  1900. // ====================================================
  1901. // ========== BEGIN photobucketAccount class ==========
  1902. // ====================================================
  1903.  
  1904. function photobucketAccount()
  1905. {
  1906.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  1907.                            .getService(Ci.flockIAccountUtils);
  1908.   this.service = Cc[PHOTOBUCKET_CONTRACTID]
  1909.                  .getService(Ci.flockIMediaWebService);
  1910.   this._coop = Components.classes["@flock.com/singleton;1"]
  1911.                          .getService(Ci.flockISingleton)
  1912.                          .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  1913.                          .wrappedJSObject;
  1914.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  1915.   this.logger.init("photobucket");
  1916.   if (!gPhotobucketAPI) {
  1917.     gPhotobucketAPI = new PhotobucketAPI();
  1918.   }
  1919.   this.api = gPhotobucketAPI;
  1920.   this._ctk = {
  1921.     interfaces: [
  1922.       "nsISupports",
  1923.       "flockIWebServiceAccount",
  1924.       "flockIMediaWebServiceAccount",
  1925.       "flockIMediaUploadAccount"
  1926.     ]
  1927.   };
  1928.   getCompTK().addAllInterfaces(this);
  1929. }
  1930.  
  1931. // BEGIN flockIWebServiceAccount interface
  1932. photobucketAccount.prototype.urn = null;
  1933.  
  1934. photobucketAccount.prototype.login =
  1935. function photobucketAccount_login(aListener)
  1936. {
  1937.   this.logger.debug("{flockIWebServiceAccount}.login() urn=" + this.urn);
  1938.  
  1939.   var timer = Components.classes["@mozilla.org/timer;1"]
  1940.                         .createInstance(Ci.nsITimer);
  1941.   var inst = this;
  1942.   timerFunc = {
  1943.     notify: function(aTimer) {
  1944.       inst.api.doLogin(inst.urn, aListener);
  1945.     }
  1946.   }
  1947.   timer.initWithCallback(timerFunc, 0, 0);  //re-check token
  1948. }
  1949.  
  1950. photobucketAccount.prototype.activate =
  1951. function photobucketAccount_activate(aListener)
  1952. {
  1953.   this.logger.debug("{flockIWebServiceAccount}.activate() urn=" + this.urn);
  1954.   var acctCoopObj = this._coop.get(this.urn);
  1955.   var inst = this;
  1956.   var listener = {
  1957.     onSuccess: function() {
  1958.       inst.logger.debug("activate() listener onAuth()");
  1959.       acctCoopObj.isPollable = true;
  1960.       aListener.onSuccess(inst, "accountAuthorized");
  1961.       try {
  1962.         var authUser = inst.api.mAuthUser;
  1963.         acctCoopObj.name = authUser.fullname;
  1964.         var photostreamURN = "urn:flock:stream:photo:photobucket:"+acctCoopObj.accountId;
  1965.         var photostream = inst._coop.get(photostreamURN);
  1966.         photostream.name = authUser.fullname+" Photostream";
  1967.       } catch (ex) {
  1968.         inst.logger.error(ex + '::' + ex.lineNumber);
  1969.       }
  1970.     },
  1971.     onError: function(aError) {
  1972.       inst.logger.debug("activate() listener onError()");
  1973.       try {
  1974.         var authUser = inst.getAuthUser();
  1975.         var acctCoopObj = inst._coop.get(inst.urn);
  1976.         acctCoopObj.isAuthenticated = false;
  1977.       } catch (ex) {
  1978.         // Don't really need to do anything, we were just trying to be nice
  1979.         // anyway...
  1980.       }
  1981.     }
  1982.   }
  1983.   this.login(listener);
  1984.   // Pre-emptively setting isAuthenticated to TRUE in order to avoid further
  1985.   // auth attemps while this one is completing.
  1986.   acctCoopObj.isAuthenticated = true;
  1987. }
  1988.  
  1989. photobucketAccount.prototype.deactivate =
  1990. function photobucketAccount_deactivate()
  1991. {
  1992.   this.logger.debug("{flockIWebServiceAccount}.deactivate()");
  1993.   var acctCoopObj = this._coop.get(this.urn);
  1994.   acctCoopObj.isPollable = false;
  1995.   acctCoopObj.isAuthenticated = false;
  1996. }
  1997.  
  1998. photobucketAccount.prototype.keep =
  1999. function photobucketAccount_keep()
  2000. {
  2001.   var c_acct = this._coop.get(this.urn);
  2002.   c_acct.isTransient = false;
  2003. }
  2004. // END flockIWebServiceAccount interface
  2005.  
  2006. // ========== END photobucketAccount class ==========
  2007.